#include "MyList.h"

template<class T>
inline void MyList<T>::for_each(void(*func)(T*))
{
	for (auto node = head; node != nullptr; node = node->next)
	{
		printf("UID:%d\n", node->UID);
		func(node->data);
	}
}

template<class T>
inline void MyList<T>::for_each_cpp11(std::function<void(T*, ListNode*)> func)
{
	for (auto node = head; node != nullptr; node = node->next)
	{
		printf("UID:%d\n", node->UID);
		func(node->data, node);
	}
}

template<class T>
inline auto MyList<T>::search(bool(*conform)(T*, void*), void* userData)
{
	for (auto node = head; node != nullptr; node = node->next)
	{
		if (conform(node->data, userData))
		{
			return node;
		}
	}
	return (MyList<T>::ListNode*)nullptr;
}

template<class T>
inline auto MyList<T>::find(int UID)
{
	for (auto i = head; i != nullptr; i = i->next)
	{
		if (i->UID == UID)
		{
			return i;
		}
	}
	return (MyList<T>::ListNode*)nullptr;
}



template<class T>
inline auto MyList<T>::fuzzy_search(bool(*conform)(T*, void*), void* userData)
{
	MyList<T>* sublist = new MyList();
	for (auto node = head; node != nullptr; node = node->next)
	{
		if (conform(node->data, userData))
		{
			sublist->push_back(*(node->data), node->UID);
		}
	}
	return sublist;
}

template<class T>
inline bool MyList<T>::insert(T* data, bool(*cmp)(T* list_data, T* data), bool(*check)(T* list_data, T* data))
{
	if (head == nullptr)
	{
		head = new ListNode;
		head->UID = cntUID;
		head->data = data;
		tail = head;
		cntUID++;
		return true;
	}

	if (check)
	{
		auto flag = true;
		for (auto node = head; node != nullptr; node = node->next)
		{
			if (!check(node->data, data))
			{
				flag = false;
			}
		}
		if (!flag) return false;
	}

	auto node = head;
	while (node->next != nullptr && cmp(node->data, data))
	{
		node = node->next;
	}

	if (node != tail)
	{
		auto pPrev = node->prev;
		auto newnode = new ListNode;
		newnode->UID = cntUID;
		newnode->data = data;
		if (pPrev) newnode->prev = pPrev;
		newnode->next = node;
		if (pPrev) pPrev->next = newnode;
		node->prev = newnode;
		if (node == head) head = newnode;
	}
	else
	{	
		ListNode* node = new ListNode;
		node->UID = cntUID;
		node->data = data;
		if (cmp(tail->data, data))
		{
			// Add the data after the tail
			node->prev = tail;
			tail->next = node;
			tail = node;
		}
		else
		{
			// Add the data before the tail
			if (tail != head)
			{
				node->prev = tail->prev;
				node->next = tail;
				node->prev->next = node;
				tail->prev = node;
			}
			else
			{
				node->next = tail;
				tail->prev = node;
				head = node;
			}
		}
	}
	cntUID++;
	return true;
}

template<class T>
inline bool MyList<T>::insert(T* data, int UID, bool(*cmp)(T* list_data, T* data), bool(*check)(T* list_data, T* data))
{
	if (head == nullptr)
	{
		head = new ListNode;
		head->UID = UID;
		head->data = data;
		tail = head;
		return true;
	}

	if (check)
	{
		auto flag = true;
		for (auto node = head; node != nullptr; node = node->next)
		{
			if (!check(node->data, data))
			{
				flag = false;
			}
		}
		if (!flag) return false;
	}

	auto node = head;
	while (node->next != nullptr && cmp(node->data, data))
	{
		node = node->next;
	}

	if (node != tail)
	{
		auto pPrev = node->prev;
		auto newnode = new ListNode;
		newnode->UID = UID;
		newnode->data = data;
		if (pPrev) newnode->prev = pPrev;
		newnode->next = node;
		if (pPrev) pPrev->next = newnode;
		node->prev = newnode;
		if (node == head) head = newnode;
	}
	else
	{
		ListNode* node = new ListNode;
		node->UID = UID;
		node->data = data;
		if (cmp(tail->data, data))
		{
			// Add the data after the tail
			node->prev = tail;
			tail->next = node;
			tail = node;
		}
		else
		{
			// Add the data before the tail
			if (tail != head)
			{
				node->prev = tail->prev;
				node->next = tail;
				node->prev->next = node;
				tail->prev = node;
			}
			else
			{
				node->next = tail;
				tail->prev = node;
				head = node;
			}
		}
	}
	return true;
}

template<class T>
inline void MyList<T>::remove(int UID)
{
	for (auto node = head; node != nullptr; node = node->next)
	{
		if (node->UID == UID)
		{
			remove(node);
			return;
		}
	}
	printf("Failed: UID Not found.\n");
}

template<class T>
inline void MyList<T>::remove(ListNode* node_ptr)
{
	if (!node_ptr) return;

	if (node_ptr == head)
	{
		if (node_ptr == tail)
		{
			if (deallocate)
				deallocate(node_ptr->data);
			else
				delete node_ptr->data;
			delete node_ptr;
			head = nullptr;
			tail = nullptr;
			return;
		}
		else
		{
			head = node_ptr->next;
			if (deallocate)
				deallocate(node_ptr->data);
			else
				delete node_ptr->data;
			delete node_ptr;
			head->prev = nullptr;
			return;
		}
	}
	else if (node_ptr == tail)
	{
		tail = node_ptr->prev;
		if (deallocate)
			deallocate(node_ptr->data);
		else
			delete node_ptr->data;
		delete node_ptr;
		tail->next = nullptr;
	}
	else
	{
		auto pNext = node_ptr->next;
		pNext->prev = node_ptr->prev;
		if (deallocate)
			deallocate(node_ptr->data);
		else
			delete node_ptr->data;
		delete node_ptr;
		pNext->prev->next = pNext;
	}
}

template<class T>
inline void MyList<T>::remove_when(bool(*func)(T* data, void* userData), void* userData)
{
	while (auto node = search(func, userData))
	{
		remove(node);
	}
}

template<class T>
inline auto MyList<T>::filter_collect(bool(*filter)(T&, void*), void* userData, bool(*cmp)(T*, T*))
{
	MyList<T>* sublist = new MyList();
	for (auto node = head; node != nullptr; node = node->next)
	{
		if (filter)
		{
			if (filter(*(node->data), userData))
			{
				sublist->insert(new T(*(node->data)), node->UID, cmp, nullptr);
			}
		}
		else
		{
			sublist->insert(new T(*(node->data)), node->UID, cmp, nullptr);
		}
	}
	return sublist;
}

template<class T>
inline void MyList<T>::push_back_p(T* data)
{
	if (head == nullptr)
	{
		head = new ListNode;
		head->data = data;
		head->UID = cntUID;
		tail = head;
		cntUID++;
		return;
	}

	ListNode* node = new ListNode;
	node->UID = cntUID;
	node->data = data;
	node->prev = tail;
	tail->next = node;
	tail = node;
	cntUID++;
}

template<class T>
inline void MyList<T>::push_back(T data, int UID)
{
	if (head == nullptr)
	{
		head = new ListNode;
		head->data = new T(data);
		head->UID = UID;
		tail = head;
		return;
	}

	ListNode* node = new ListNode;
	node->data = new T(data);
	node->UID = UID;
	node->prev = tail;
	tail->next = node;
	tail = node;
}

template<class T>
inline void MyList<T>::push_back(T data)
{
	if (head == nullptr)
	{
		head = new ListNode;
		head->data = new T(data);
		head->UID = cntUID;
		tail = head;
		cntUID++;
		return;
	}

	ListNode* node = new ListNode;
	node->data = new T(data);
	node->UID = cntUID;
	node->prev = tail;
	tail->next = node;
	tail = node;
	cntUID++;
}

template<class T>
inline void MyList<T>::destory()
{
	if (head != nullptr)
	{
		// Step 1: clear the data.
		for (auto n = head; n != nullptr; n = n->next)
		{
			if (deallocate)
			{
				deallocate(n->data);
			}
			else
			{
				delete n->data;
			}
		}

		// Step 2: clear nodes.
		auto n = head;
		auto pNext = n->next;
		while (n != nullptr && pNext != nullptr)
		{
			delete n;
			n = pNext;
			if (n->next != nullptr)
				pNext = n->next;
			else
				pNext = nullptr;
		}
	}
}

template<class T>
inline size_t MyList<T>::size()
{
	size_t len = 0;
	for (auto a = head; a != nullptr; a = a->next)
	{
		len++;
	}
	return len;
}